﻿#include "CModBusOper.h"
#include <QEventLoop>
#include <QTimer>
#include <QVariant>
#include <QDebug>
CModBusOper::CModBusOper(QString sIp, int nPort,int nType)
{
	m_sIp = sIp;
	m_nPort = nPort;
	m_pLockRw = new QMutex();
	m_pMutexDate = new QMutex();
	m_pLockRwWait = new QMutex();
	m_pSysMutex = new QMutex();
	m_pLock = new QMutex(QMutex::Recursive);
	m_pWaitCondition = new QWaitCondition();
	m_bThread = true;
	m_enThreadCmd = enReadAndWrite;
	m_nReadIndex = 0;
	m_pThread = new QThread();
	this->moveToThread(m_pThread);
	connect(m_pThread, SIGNAL(started()), this, SLOT(slot_Work()));
	m_pThread->start();
}

void CModBusOper::setSlaveId(int nId)
{
}

CModBusOper::~CModBusOper()
{
	threadStop();
}

void CModBusOper::threadStop()
{
	if (m_bThread)
	{
		m_bThread = false;
		m_pThread->quit();
		if (!m_pThread->wait(5000))
		{
			m_pThread->terminate();
		}
	}
	if (m_pModTcp)
	{
		m_pModTcp->deleteLater();
		m_pModTcp = nullptr;
	}
	
}
bool CModBusOper::ReadVal(int nAddr, int &nReVal)
{
	if(!m_pLock->tryLock(1000))
	{
		return false;
	}
	SModData cModData;
	cModData.resetData();
	cModData.nAddr = nAddr;
	cModData.nLength = 1;
	setModDataWait(cModData);
	if (!readValsOper())
	{
		m_pLock->unlock();
		return false;
	}
	cModData = getModDataWait();
	if (cModData.vecReadVal.size() > 0)
	{
		nReVal = cModData.vecReadVal[0];
		m_pLock->unlock();
		return true;
	}
	else
	{
		nReVal = -1;
		m_pLock->unlock();
		return false;
	}
	
}

bool CModBusOper::ReadVal(int nAddr, int nLength, int &nReVal)
{
	if (!m_pLock->tryLock(1000))
	{
		return false;
	}
	SModData cModData;
	cModData.resetData();
	cModData.nAddr = nAddr;
	cModData.nLength = nLength;
	setModDataWait(cModData);

	if (!readValsOper())
	{
		m_pLock->unlock();
		return false;
	}
	cModData = getModDataWait();
	if (cModData.vecReadVal.size() > 0)
	{
		if (cModData.vecReadVal.size() > 0)
		{
			for (int i = 0; i < cModData.vecReadVal.size(); i++)
			{
				nReVal = nReVal * 65536 + cModData.vecReadVal[i];
			}
		}
		else
		{
			nReVal = -1;
		}
		m_pLock->unlock();
		return true;
	}
	else
	{
		nReVal = -1;
		m_pLock->unlock();
		return false;
	}
}

bool CModBusOper::ReadValEx(int nAddr, int nLength, QVector<int>&vecVal)
{
	QVector<int>vecAddr;
	int nCount = nLength / 100;
	int nLastLength = nLength % 100;
	for (int i = 0; i < nCount; i++)
	{
		SModData cModDataAdd;
		cModDataAdd.resetData();
		cModDataAdd.nAddr = nAddr + i * 100;
		cModDataAdd.nLength = 100;
		addModData(cModDataAdd);
		vecAddr.push_back(cModDataAdd.nAddr);
	}
	if (nLastLength != 0)
	{
		SModData cModDataAddLast;
		cModDataAddLast.resetData();
		cModDataAddLast.nAddr = nAddr + nCount * 100;
		cModDataAddLast.nLength = nLastLength;
		addModData(cModDataAddLast);
		vecAddr.push_back(cModDataAddLast.nAddr);
	}
	
	SModData cModData;
	for (int i = 0; i < vecAddr.size(); i++)
	{
		cModData= getModDataMap(vecAddr[i]);
		vecVal.append(cModData.vecReadVal);
	}
	if (vecVal.size() == nLength)
	{
		return true;
	}
	else
	{
		return false;
	}		
}

bool CModBusOper::ReadValExLoop(int nAddr, int nLength, QVector<int>& vecVal)
{
	int nCount = nLength / 100;
	int nLastLength = nLength % 100;
	for (int i = 0; i < nCount; i++)
	{
		SModData cModDataAdd;
		cModDataAdd.resetData();
		cModDataAdd.nAddr = nAddr + i * 100;
		cModDataAdd.nLength = 100;
		if (ReadValLoop(cModDataAdd))
		{
			vecVal.append(cModDataAdd.vecReadVal);
		}
		else
		{
			return false;
		}
	}
	if (nLastLength != 0)
	{
		SModData cModDataAddLast;
		cModDataAddLast.resetData();
		cModDataAddLast.nAddr = nAddr + nCount * 100;
		cModDataAddLast.nLength = nLastLength;
		if (ReadValLoop(cModDataAddLast))
		{
			vecVal.append(cModDataAddLast.vecReadVal);
		}
		else
		{
			return false;
		}
	}
	if (vecVal.size() == nLength)
	{
		return true;
	}
	else
	{
		return false;
	}
}

bool CModBusOper::WriteVal(int nAddr, int nVal)
{
	if (!m_pLock->tryLock(1000))
	{
		return false;
	}

	SModData cModData;
	cModData.nAddr = nAddr;
	cModData.vecWriteVal.push_back(nVal);
	cModData.nLength = 1;
	setModDataWait(cModData, 1);
	if (!writeValsOper())
	{
		m_pLock->unlock();
		return false;
	}
	else
	{
		m_pLock->unlock();
		return true;
	}
}

bool CModBusOper::WriteValCoil(int nAddr, int)
{
	return false;
}

bool CModBusOper::WriteVal(int nAddr, int nLength, int nVal)
{
	if (!m_pLock->tryLock(1000))
	{
		return false;
	}
	QVector<int> vecVal;
	vecVal.resize(nLength);
	for (int i = nLength - 1; i >= 0; i--)
	{
		vecVal[i] = nVal % 65536;
		nVal = (nVal / 65536);
	}
	SModData cModData;
	cModData.nAddr = nAddr;
	cModData.vecWriteVal= vecVal;
	cModData.nLength = nLength;
	setModDataWait(cModData, 1);
	if (!writeValsOper())
	{
		m_pLock->unlock();
		return false;
	}
	else
	{
		m_pLock->unlock();
		return true;
	}
}

bool CModBusOper::WriteValEx(int nAddr, int nLength, QVector<int>vecVal)
{
	if (!m_pLock->tryLock(1000))
	{
		return false;
	}
	int nCount = nLength / 100;
	int nLastLength = nLength % 100;
	for (int i = 0; i < nCount; i++)
	{
		SModData cModData;
		cModData.resetData();
		cModData.nAddr = nAddr + i * 100;
		cModData.nLength = 100;

		for (int j = 0; j < cModData.nLength; j++)
		{
			cModData.vecWriteVal.push_back(vecVal[j+i*100]);
		}
		setModDataWait(cModData, 1);

		if (!writeValsOper())
		{
			m_pLock->unlock();
			return false;
		}

	}
	if (nLastLength != 0)
	{
		SModData cModData;
		cModData.resetData();
		cModData.nAddr = nAddr + nCount * 100;
		cModData.nLength = nLastLength;
		for (int j = 0; j < cModData.nLength; j++)
		{
			cModData.vecWriteVal.push_back(vecVal[j+nCount*100]);
		}
		setModDataWait(cModData, 1);

		if (!writeValsOper())
		{
			m_pLock->unlock();
			return false;
		}
	}
	m_pLock->unlock();
	return true;
}

QString CModBusOper::GerReError()
{
	return QString();
}

void CModBusOper::initMod()
{
	m_pModTcp = new QModbusTcpClient(this);
	QVariant vIp = m_sIp;
	QVariant vPort = m_nPort;
	m_pModTcp->setConnectionParameter(QModbusDevice::NetworkPortParameter, vPort);
	m_pModTcp->setConnectionParameter(QModbusDevice::NetworkAddressParameter, vIp);
	m_pModTcp->setTimeout(3000);
	m_pModTcp->setNumberOfRetries(3);
	if (m_pModTcp->connectDevice())
	{
		m_bModConnect = true;
	}
}

bool CModBusOper::ReadValLoop(SModData& cModData)
{
	if (!m_pLock->tryLock(1000))
	{
		return false;
	}
	setModDataWait(cModData);

	if (!readValsOper())
	{
		m_pLock->unlock();
		return false;
	}
	cModData = getModDataWait();
	m_pLock->unlock();
}


bool CModBusOper::readValsOper()
{
	for (int i = 0; i < 3; i++)
	{
		m_enThreadCmd = enReadWait;
		waitSys(1000);
		SModData cModData = getModDataWait();
		if (cModData.bExecSuc)
		{
			return true;
		}
	}
	return false;
}

bool CModBusOper::writeValsOper()
{
	for (int i = 0; i < 3; i++)
	{
		m_enThreadCmd = enWriteWait;
		waitSys(1000);
		SModData cModData = getModDataWait(1);
		if (cModData.bExecSuc)
		{
			return true;
		}
	}
	return false;
}


bool CModBusOper::threadReadVal(SModData & cModData)
{
	QList<QModbusReply *> pendingReplies = m_pModTcp->findChildren<QModbusReply *>();
	for (QModbusReply *replyEx : pendingReplies)
	{
		if (replyEx)
			replyEx->deleteLater(); // 清理未处理的回复
	}
	QModbusDataUnit ReadUnit = QModbusDataUnit(QModbusDataUnit::HoldingRegisters, cModData.nAddr - 40001, cModData.nLength);  //设置寄存器类型、地址、寄存器个数;
	if (auto *reply = m_pModTcp->sendReadRequest(ReadUnit, cModData.nSlaveId)) //1表示modbus从站地址，在本系统中，只有一个从站，地址为1
	{
		if (reply->isFinished())
		{
			reply->deleteLater();
			return false;
		}
		QEventLoop loop;
		QTimer timeoutTimer;
		timeoutTimer.setSingleShot(true);
		connect(&timeoutTimer, &QTimer::timeout, &loop, &QEventLoop::quit);
		connect(reply, &QModbusReply::finished, &loop, &QEventLoop::quit);
		timeoutTimer.start(400);
		loop.exec();
		if (!timeoutTimer.isActive())
		{
			reply->deleteLater();
			return false;
		}
		timeoutTimer.stop();
		if (QModbusDevice::NoError == reply->error())
		{
			QModbusDataUnit unit = reply->result();
			for (uint i = 0; i < unit.valueCount(); i++)
			{
				cModData.vecReadVal.push_back(unit.value(i));
			}
			reply->deleteLater();
			return true;
		}
		else
		{
			reply->deleteLater();
			return false;
		}
	}
	else
	{
		return false;
	}
}

bool CModBusOper::threadWriteVal(SModData & cModData)
{
	QList<QModbusReply *> pendingReplies = m_pModTcp->findChildren<QModbusReply *>();
	for (QModbusReply *replyEx : pendingReplies)
	{
		if (replyEx)
			replyEx->deleteLater(); // 清理未处理的回复
	}
	QModbusDataUnit WriteUnit(QModbusDataUnit::HoldingRegisters, cModData.nAddr - 40001, cModData.nLength);//设置寄存器类型、地址、寄存器个数
	for (quint16 i = 0; i < cModData.nLength; i++)
	{
		WriteUnit.setValue(i, cModData.vecWriteVal[i]);
	}
	if (auto *reply = m_pModTcp->sendWriteRequest(WriteUnit, cModData.nSlaveId))
	{
		if (reply->isFinished())
		{
			reply->deleteLater(); // 清理未处理的回复
			return false;
		}
		QEventLoop loop;
		QTimer timeoutTimer;
		timeoutTimer.setSingleShot(true);
		connect(&timeoutTimer, &QTimer::timeout, &loop, &QEventLoop::quit);
		connect(reply, &QModbusReply::finished, &loop, &QEventLoop::quit);
		timeoutTimer.start(400);
		loop.exec();
		if (!timeoutTimer.isActive())
		{
			reply->deleteLater(); // 清理未处理的回复
			return false;
		}
		timeoutTimer.stop();
		if (QModbusDevice::NoError == reply->error())
		{
			reply->deleteLater();
			return true;
		}
		else
		{
			reply->deleteLater();
			return false;
		}
	}
	else
	{
		return false;
	}
}



void CModBusOper::sleepSysMs(int nMs)
{
	int nCount = 0;
	if (nMs > 50)
	{
		nCount = nMs / 50;
		for (int i = 0; i < nCount; i++)
		{
			QEventLoop loop;
			QTimer::singleShot(50, &loop, SLOT(quit()));
			loop.exec();
			if (m_enThreadCmd == enReadWait || m_enThreadCmd == enWriteWait)
			{
				break;
			}
		}
	}
	else
	{
		QEventLoop loop;
		QTimer::singleShot(nMs, &loop, SLOT(quit()));
		loop.exec();
	}
}

bool CModBusOper::waitSys(int nSystime)
{
	m_pSysMutex->lock();
	if (m_pWaitCondition->wait(m_pSysMutex, nSystime))
	{
		m_pSysMutex->unlock();
		return true;
	}
	else
	{
		m_pSysMutex->unlock();
		return false;
	}
}

void CModBusOper::execRead()
{
	//取一个SModData对象
	SModData cModData = getModData(m_nReadIndex);
	if (cModData.nLength <= 0)
	{
		return;
	}

	//读成功
	if (threadReadVal(cModData))
	{
		//更新读数据的缓存
		setModDataMap(cModData);
		m_nReadIndex++;
	}
	//读失败
	else
	{
		//失败次数+1
		cModData.nRetryCount++;
		//大于三次读下一个
		if (cModData.nRetryCount >= 3)
		{
			m_nReadIndex++;
		}
		else
		{
			//更新数组m_vecModDataR
			setModData(m_nReadIndex,cModData);
		}
	}
}

void CModBusOper::execWrite()
{
	SModData cModData = getModData(0, 1);
	if (cModData.nLength <= 0)
	{
		return;
	}
	//写成功
	if (threadWriteVal(cModData))
	{
		removeModDataW(0);
	}
	else
	{
		cModData.nRetryCount++;
		if (cModData.nRetryCount >= 3)
		{
			removeModDataW(0);
		}
		else
		{
			setModData(0, cModData, 1);
		}
	}
}

void CModBusOper::execReadWait()
{
	SModData cModData = getModDataWait();
	if (threadReadVal(cModData))
	{
		cModData.bExecSuc = true;
	}
	else
	{
		cModData.bExecSuc = false;
	}
	setModDataWait(cModData);
	//执行读写事件
	m_enThreadCmd = enReadAndWrite;
	m_pWaitCondition->wakeAll();

}

void CModBusOper::execWriteWait()
{
	SModData cModData = getModDataWait(1);
	if (threadWriteVal(cModData))
	{
		cModData.bExecSuc = true;
	}
	else
	{
		cModData.bExecSuc = false;
	}
	setModDataWait(cModData, 1);
	//执行读写事件
	m_enThreadCmd = enReadAndWrite;
	m_pWaitCondition->wakeAll();
}

void CModBusOper::setModDataWait(SModData cModData, int nType)
{
	m_pLockRwWait->lock();
	if (nType == 0)
	{
		m_cModDataR = cModData;
	}
	else
	{
		m_cModDataW = cModData;
	}
	m_pLockRwWait->unlock();
}

SModData CModBusOper::getModDataWait(int nType)
{
	m_pLockRwWait->lock();
	SModData cModData;
	if (nType == 0)
	{
		cModData = m_cModDataR;
	}
	else
	{
		cModData = m_cModDataW;
	}
	m_pLockRwWait->unlock();
	return cModData;
}

void CModBusOper::addModData(SModData cModData, int nType)
{
	if (nType == 0)
	{
		m_pMutexDate->lock();
		if (m_mapModDataR.contains(cModData.nAddr))
		{
			m_pMutexDate->unlock();
			return;
		}
		m_mapModDataR.insert(cModData.nAddr, cModData);
		m_pMutexDate->unlock();
		m_pLockRw->lock();
		m_vecModDataR.push_back(cModData);
		m_pLockRw->unlock();
	}
	else
	{
		bool bAdd = true;
		m_pLockRw->lock();
		for (int i = 0; i < m_vecModDataW.size(); i++)
		{
			if (cModData.nAddr == m_vecModDataW[i].nAddr)
			{
				bAdd = false;
				break;
			}			
		}
		if (bAdd)
		{
			m_vecModDataW.push_back(cModData);
		}
		m_pLockRw->unlock();
	}
	
}

void CModBusOper::setModData(int nIndex, SModData cModData, int nType)
{
	if (nType == 0)
	{
		m_pLockRw->lock();
		if (nIndex < m_vecModDataR.size())
		{
			m_vecModDataR[nIndex] = cModData;
		}
		m_pLockRw->unlock();
	}
	else
	{
		m_pLockRw->lock();
		if (nIndex < m_vecModDataW.size())
		{
			m_vecModDataW[nIndex] = cModData;
		}
		m_pLockRw->unlock();
	}
}

void CModBusOper::removeModDataW(int nIndex)
{
	m_pLockRw->lock();
	if (nIndex < m_vecModDataW.size())
	{
		m_vecModDataW.remove(nIndex);
	}
	m_pLockRw->unlock();
}

void CModBusOper::removeModDataR(int nIndex)
{
	SModData cModData;
	m_pLockRw->lock();
	if (nIndex >= m_vecModDataR.size())
	{
		m_pLockRw->unlock();
		return;		
	}
	cModData = m_vecModDataR[nIndex];
	m_vecModDataR.remove(nIndex);
	m_pLockRw->unlock();
	m_pMutexDate->lock();
	if (!m_mapModDataR.contains(cModData.nAddr))
	{
		m_pMutexDate->unlock();
		return;
	}
	m_mapModDataR.remove(cModData.nAddr);
	m_pMutexDate->unlock();
}


SModData CModBusOper::getModData(int nIndex, int nType)
{
	SModData cModData;
	if (nType == 0)
	{
		m_pLockRw->lock();
		if (m_vecModDataR.size() <= 0)
		{
			m_pLockRw->unlock();
			return cModData;
		}

		if (nIndex < m_vecModDataR.size())
		{
			cModData = m_vecModDataR[nIndex];
		}
		else
		{
			m_nReadIndex = 0;
			//复位所有失败的计数
			for (int i = 0; i < m_vecModDataR.size(); i++)
			{
				m_vecModDataR[i].resetData();
			}
			cModData = m_vecModDataR[0];
			
		}
		m_pLockRw->unlock();
	}
	else
	{
		m_pLockRw->lock();
		if (m_vecModDataR.size() <= 0)
		{
			m_pLockRw->unlock();
			return cModData;
		}
		if (nIndex < m_vecModDataW.size())
		{
			cModData = m_vecModDataW[nIndex];
		}
		m_pLockRw->unlock();
	}
	return cModData;
}

void CModBusOper::setModDataMap(SModData cModData)
{
	m_pMutexDate->lock();
	if (!m_mapModDataR.contains(cModData.nAddr))
	{
		m_pMutexDate->unlock();
		return;
	}
	m_mapModDataR[cModData.nAddr] = cModData;
	m_pMutexDate->unlock();
}

SModData CModBusOper::getModDataMap(int nAddr)
{
	SModData cModData;
	m_pMutexDate->lock();
	if (!m_mapModDataR.contains(nAddr))
	{
		m_pMutexDate->unlock();
		return cModData;
	}
	cModData = m_mapModDataR[nAddr];
	m_pMutexDate->unlock();
	return cModData;
}

int CModBusOper::getModDataSize(int nType)
{
	int nSize = 0;
	m_pLockRw->lock();
	if (nType == 0)
	{
		nSize = m_vecModDataR.size();
	}
	else
	{
		nSize = m_vecModDataW.size();
	}
	m_pLockRw->unlock();
	return nSize;
}


void CModBusOper::slot_Work()
{
	initMod();
	int nWriteSize = 0;
	int nConIndex = 0;  //重连计数
	while (m_bThread)
	{

		if (nConIndex >= 100)
		{
			if (m_pModTcp->state() != QModbusDevice::ConnectedState)
			{
				if (m_pModTcp->connectDevice())
				{
					m_bModConnect = true;
				}
			}
			nConIndex = 0;
		}
		nConIndex++;

		switch (m_enThreadCmd)
		{
		case enNone:          //无事件
			sleepSysMs(100); 
			break;
		case enReadAndWrite:  //读写事件
			nWriteSize = getModDataSize(1);
			if (m_vecModDataW.size() > 0)
			{
				execWrite();
			}
			else
			{
				execRead();				
			}
			sleepSysMs(200);
			break;
		case enReadWait:      //阻塞读
			execReadWait();
			break;
		case enWriteWait:     //阻塞写
			execWriteWait();
			break;
		case 4:
			break;			
		}
		sleepSysMs(50);
		
	}
}

